package com.minelx.httpclient.core.request;

import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.Map;

import static com.minelx.util.IOUtil.bytesOf;
import static java.util.stream.Collectors.toMap;

public interface HttpRequestFactory {

	HttpRequestBase createRequest();

	default IResponse fetch() {
		return fetch(8000);
	}

	default IResponse fetch(int connectTimeout) {
		RequestConfig config = RequestConfig.custom()
				.setConnectTimeout(connectTimeout)
				.setSocketTimeout(connectTimeout)
				.setConnectionRequestTimeout(connectTimeout)
				.build();
		try (CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(config).build();
			 CloseableHttpResponse response = client.execute(createRequest());
			 InputStream contentAsStream = response.getEntity().getContent()) {
			return IResponse.of(bytesOf(contentAsStream), headersOf(response));
		} catch (SocketTimeoutException e) {
			throw new RequestTimeoutException(e);
		} catch (IOException e) {
			throw new IllegalStateException("error while launching request.", e);
		}
	}

	default IResponse fetchTilIGetIt(int connectTimeout, int retryingCount, long delayed) {
		for (int turn = 1; turn <= retryingCount + 1; turn++) {
			try {
				return fetch(connectTimeout);
			} catch (RequestTimeoutException ignored) {
				System.err.println("retrying turn: " + turn + ", let me have a rest...");
				sleep(delayed);
			}
		}
		throw new IllegalStateException("requests are still failed after retrying.");
	}

	static void sleep(long delayed) {
		try {
			Thread.sleep(delayed);
		} catch (InterruptedException e) {
			throw new RuntimeException("error while sleeping...", e);
		}
	}

	static Map<String, String> headersOf(CloseableHttpResponse response) {
		return Arrays.stream(response.getAllHeaders())
				.collect(toMap(NameValuePair::getName, NameValuePair::getValue));
	}
}
